/*
 * Copyright 2013-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.tinem.platform.web.gateway.filter;

import cn.hutool.core.util.NumberUtil;
import com.tinem.platform.module.pojo.co.GatewayHeadName;
import com.tinem.platform.web.gateway.util.GatewayUtil;
import com.tinem.platform.web.gateway.util.MDCUtil;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.slf4j.MDC;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;

/**
 * GatewayFilter that modifies the respons body.
 */
@Slf4j
@Component
public class LogMdcClearGatewayFilterFactory extends
        AbstractGatewayFilterFactory<Object> {

    @Resource
    StringRedisTemplate stringRedisTemplate;

    @Override
    public GatewayFilter apply(Object config) {
        ModifyResponseGatewayFilter gatewayFilter = new ModifyResponseGatewayFilter();
        return gatewayFilter;
    }
    public class ModifyResponseGatewayFilter implements GatewayFilter, Ordered {
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            return chain.filter(exchange.mutate().response(decorate(exchange)).build());
        }

        @SuppressWarnings("unchecked")
        ServerHttpResponse decorate(ServerWebExchange exchange) {
            return new ServerHttpResponseDecorator(exchange.getResponse()) {

                @Override
                public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                    this.log(exchange);
                    return super.writeWith(body);
                }

                @Override
                public Mono<Void> writeAndFlushWith(
                        Publisher<? extends Publisher<? extends DataBuffer>> body) {
                    this.log(exchange);
                    return super.writeAndFlushWith(body);
                }
                private void log(ServerWebExchange exchange){
                    MDCUtil.set(exchange);
                    long reqTimestamp = GatewayUtil.getReqTimestamp(exchange);
                    long reqAcceptTimestamp = GatewayUtil.getReqAcceptTimestamp(exchange);
                    long resTimestamp = NumberUtil.parseLong(exchange.getResponse().getHeaders().getFirst(GatewayHeadName.X_PLATFORM_GATEWAY_RES_TIMESTAMP));
                    long connectionTime = 0;
                    long handleTime = 0;
                    try{
                        connectionTime = reqAcceptTimestamp - reqTimestamp;
                        handleTime = resTimestamp - reqAcceptTimestamp;
                    }catch (Exception e){
                        log.error("",e);
                    }
                    log.info("connection time: ({}); handle time: ({}); code: ({}); message: ({}); business code:({}); business message:({})"
                            ,connectionTime
                            ,handleTime
                            ,exchange.getResponse().getHeaders().getFirst(GatewayHeadName.X_PLATFORM_GATEWAY_RES_CODE)
                            ,exchange.getResponse().getHeaders().getFirst(GatewayHeadName.X_PLATFORM_GATEWAY_RES_MESSAGE)
                            ,exchange.getResponse().getHeaders().getFirst(GatewayHeadName.X_PLATFORM_RES_CODE)
                            ,exchange.getResponse().getHeaders().getFirst(GatewayHeadName.X_PLATFORM_RES_MESSAGE)
                    );
                    MDC.remove(GatewayHeadName.X_PLATFORM_GATEWAY_REQUEST_ID);
                    MDC.remove(GatewayHeadName.X_PLATFORM_GATEWAY_CONTEXT_JTI);
                }
            };
        }

        @Override
        public int getOrder() {
            return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 3;
        }

    }
}
